package ar.edu.itba.cg.model;

import java.util.ArrayList;
import java.util.List;

import ar.edu.itba.cg.Bowling;
import ar.edu.itba.cg.util.TextureUtil;

import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.light.DirectionalLight;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Node;
import com.jme.scene.Skybox;
import com.jme.scene.Spatial;
import com.jme.scene.Spatial.TextureCombineMode;
import com.jme.scene.shape.Box;
import com.jme.scene.state.CullState;
import com.jme.scene.state.FogState;
import com.jme.scene.state.LightState;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;
import com.jme.util.Timer;
import com.jmex.physics.DynamicPhysicsNode;
import com.jmex.physics.PhysicsSpace;
import com.jmex.physics.StaticPhysicsNode;
import com.jmex.physics.material.Material;

public class Scene {
	private static final float farPlane= 2000.0f;
	private static final int SCENE_SIZE = 320;
	private final static int WALL_TALL=80;
	
	/* Court Parameters */
	private final static int COURT_LARGE= SCENE_SIZE - SCENE_SIZE/5;
	private final static int COURT_WIDHT= 45;
	private final static int COURT_THICK= 6;
	private final static int COURT_FLOOR= -WALL_TALL + COURT_THICK + 21;
	
	/* Gutter parameters */
	private final static int GUTTER_WIDHT= 25;
	private final static int GUTTER_LARGE_EXTRA=  40;
	private final static int GUTTER_LARGE= COURT_LARGE + GUTTER_LARGE_EXTRA;
	
	/* Pins Parameters */
	private final static int PINS_COUNT= 10;
	private final static int PINS_SITE= COURT_LARGE - 60;
	private final static int PINS_SPACE=11;
	
	/* Ball Parameters */
	private final static int BALL_SITE= COURT_LARGE - 10;
	
	
	/* Game parameters */
	private Node rootNode;
	private Skybox skybox;
	private LightState lightState;
	private DisplaySystem display;
	private Camera cam;
	private PhysicsSpace physicsSpace;
	
	/* Scene objects */
	private StaticPhysicsNode pbGutterL;
	private StaticPhysicsNode pbGutterR;
	private StaticPhysicsNode pbGutterF;
	private StaticPhysicsNode pbfloor;
	private StaticPhysicsNode pbFrontWall;
	
	/* Play Objects */
	private Ball ball;
	private List<Pin> pinList;

	public Scene(DisplaySystem display, Camera cam, Node rootNode, LightState lightState, PhysicsSpace physicsSpace){
		this.rootNode= rootNode;
		this.display= display;
		this.lightState= lightState;
		this.cam= cam;
		this.physicsSpace= physicsSpace;
		//buildSkyBox();
		createWalls();
		createCourt();
		createPins();
		createBall();
		setupCamera();
		setupLight();

	}
	

	
	private void createWalls(){
		Node wallsNode = new Node("wallsNode");

		wallsNode.setRenderState(TextureUtil.getTexture(display, "masonry-wall-texture.jpg")); 

		/* create the front wall */
		Box wall = new Box("frontWall", new Vector3f(), SCENE_SIZE, WALL_TALL, 2);
		wall.setModelBound(new BoundingBox());
		wall.updateModelBound();
		wall.getLocalTranslation().set(0, 0, -SCENE_SIZE);
		wallsNode.attachChild(wall);

		/* create the left wall */
		wall = new Box("leftWall", new Vector3f(), 5, WALL_TALL, SCENE_SIZE);
		wall.setModelBound(new BoundingBox());
		wall.updateModelBound();
		wall.getLocalTranslation().set(SCENE_SIZE, 0, 0);
		wallsNode.attachChild(wall);

		/* create the right wall */
		wall = new Box("rightWall", new Vector3f(), 5, WALL_TALL, SCENE_SIZE);
		wall.setModelBound(new BoundingBox());
		wall.updateModelBound();
		wall.getLocalTranslation().set(-SCENE_SIZE, 0, 0);
		wallsNode.attachChild(wall);
		
		
		Node floorNode = new Node("floorNode");

		floorNode.setRenderState(TextureUtil.getTexture(display, "ConcretePlates0087_S.jpg")); 
		
		/* create the floor */
		Box floor = new Box("floor", new Vector3f(), SCENE_SIZE, 1, SCENE_SIZE);
		floor.setModelBound(new BoundingBox());
		floor.updateModelBound();
		floor.getLocalTranslation().set(0, -WALL_TALL, 0);
		floorNode.attachChild(floor);
		
		rootNode.attachChild(wallsNode);
		rootNode.attachChild(floorNode);

		
	}

	private void createCourt(){
		Node courtNode = new Node("courtNode");
		
		Node floorCourtNode= new Node("floorCourtNode");
		
		courtNode.attachChild(floorCourtNode);
		floorCourtNode.setRenderState(TextureUtil.getTexture(display, "WoodFine0005_1_S.jpg"));//wood-texture.tga 
		
		/* create the court floor */
		Box floor = new Box("floorCourt", new Vector3f(), COURT_WIDHT, COURT_THICK, COURT_LARGE);
		floor.setModelBound(new BoundingBox());
		floor.updateModelBound();
		floor.getLocalTranslation().set(0, -WALL_TALL+ COURT_THICK + 3, 0);
		pbfloor = createGutterStaticNode(floor);
		floorCourtNode.attachChild(pbfloor);
		
		/* Gutter texture */
		TextureState ts= TextureUtil.getTexture(display,"WoodPlatesNew0050_1_S.jpg");
		
		/* Gutter right wall */
		Box gutterR = new Box("gutterRightWall", new Vector3f(), 2, 12, GUTTER_LARGE);
		gutterR.setModelBound(new BoundingBox());
		gutterR.updateModelBound();
		gutterR.getLocalTranslation().set(COURT_WIDHT + GUTTER_WIDHT, -WALL_TALL+ 12 + 1, -GUTTER_LARGE_EXTRA);
		gutterR.setRenderState(ts);
		pbGutterR = createGutterStaticNode(gutterR);
		courtNode.attachChild(pbGutterR);
		
		/* Gutter left wall */
		Box gutterL = new Box("gutterRightWall", new Vector3f(), 2, 12, GUTTER_LARGE);
		gutterL.setModelBound(new BoundingBox());
		gutterL.updateModelBound();
		gutterL.getLocalTranslation().set(-COURT_WIDHT - GUTTER_WIDHT, -WALL_TALL+ 12 + 1, -GUTTER_LARGE_EXTRA);
		gutterL.setRenderState(ts);
		pbGutterL = createGutterStaticNode(gutterL);
		courtNode.attachChild(pbGutterL);
		
		/* Gutter floor */
		Box gutterF = new Box("gutterRightWall", new Vector3f(), COURT_WIDHT + GUTTER_WIDHT , 4, COURT_LARGE);
		gutterF.setModelBound(new BoundingBox());
		gutterF.updateModelBound();
		gutterF.getLocalTranslation().set(0, -WALL_TALL+6, 0);
		gutterF.setRenderState(TextureUtil.getTexture(display, "ConcretePlates0087_S.jpg"));
		pbGutterF= createGutterStaticNode(gutterF);
		courtNode.attachChild(pbGutterF);
		
		/* Front Wall */
		Box frontWall = new Box("frontWall", new Vector3f(), COURT_WIDHT + GUTTER_WIDHT , WALL_TALL/2, 1);
		frontWall.setModelBound(new BoundingBox());
		frontWall.updateModelBound();
		frontWall.getLocalTranslation().set(0, -WALL_TALL/2, -SCENE_SIZE+2);
		frontWall.setRenderState(TextureUtil.getTexture(display,"itba.gif"));
		pbFrontWall= createGutterStaticNode(frontWall);
		courtNode.attachChild(pbFrontWall);
		
		
		rootNode.attachChild(courtNode);
	}
	
	private StaticPhysicsNode createGutterStaticNode(Box gutterBox){
		StaticPhysicsNode pbGutter = physicsSpace.createStaticNode();
		pbGutter.attachChild(gutterBox);
		pbGutter.generatePhysicsGeometry();
		pbGutter.setMaterial(Material.GRANITE);
		
		return pbGutter;
	}
	
	
	private void setupCamera() {
		cam.setFrustumPerspective(50.0f, (float) display.getWidth()
				/ (float) display.getHeight(), 1f, farPlane);
		cameraAfterShoot();
	}
	
	public void cameraAfterShoot(){
		cam.setLocation(new Vector3f(0, -WALL_TALL/2 + WALL_TALL/4, SCENE_SIZE));
		cam.lookAt(new Vector3f(0,COURT_FLOOR-10, BALL_SITE/2), Vector3f.UNIT_Y);
		cam.update();
	}
	
	public void cameraBeforeShoot(){
		cam.setLocation(new Vector3f(0, 0, SCENE_SIZE/4));
		cam.lookAt(new Vector3f(0,COURT_FLOOR-10, -BALL_SITE/2), Vector3f.UNIT_Y);
		cam.update();
	}
	

	
	
	private void setupLight(){
		lightState.detachAll();
		
		/* Light to the right wall */
		DirectionalLight dLightOne = new DirectionalLight();
		dLightOne.setEnabled(true);
		dLightOne.setDiffuse(new ColorRGBA(1, 1, 1, 1));
		dLightOne.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.7f, 1));
		dLightOne.setDirection(new Vector3f(1f, -1f, 0));
		lightState.attach(dLightOne);
		
		/* Light to the lesft wall */
		DirectionalLight dLightTwo = new DirectionalLight();
		dLightTwo.setEnabled(true);
		dLightTwo.setDiffuse(new ColorRGBA(1, 1, 1, 1));
		dLightTwo.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.7f, 1));
		dLightTwo.setDirection(new Vector3f(-1f, -1f, 0));
		lightState.attach(dLightTwo);
		
		/* Light to the front wall */
		DirectionalLight dLightThree = new DirectionalLight();
		dLightThree.setEnabled(true);
		dLightThree.setDiffuse(new ColorRGBA(1, 1, 1, 1));
		dLightThree.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.7f, 1));
		dLightThree.setDirection(new Vector3f(0, -1f, -1f));
		lightState.attach(dLightThree);
		
	}
	
	private void buildSkyBox() {
		skybox = new Skybox("skybox", 10, 10, 10);

		String dir = "resources/";
		Texture north = TextureManager.loadTexture(Bowling.class
				.getClassLoader().getResource(dir + "1.jpg"),
				Texture.MinificationFilter.BilinearNearestMipMap,
				Texture.MagnificationFilter.Bilinear);
		Texture south = TextureManager.loadTexture(Bowling.class
				.getClassLoader().getResource(dir + "3.jpg"),
				Texture.MinificationFilter.BilinearNearestMipMap,
				Texture.MagnificationFilter.Bilinear);
		Texture east = TextureManager.loadTexture(Bowling.class
				.getClassLoader().getResource(dir + "2.jpg"),
				Texture.MinificationFilter.BilinearNearestMipMap,
				Texture.MagnificationFilter.Bilinear);
		Texture west = TextureManager.loadTexture(Bowling.class
				.getClassLoader().getResource(dir + "4.jpg"),
				Texture.MinificationFilter.BilinearNearestMipMap,
				Texture.MagnificationFilter.Bilinear);
		Texture up = TextureManager.loadTexture(Bowling.class
				.getClassLoader().getResource(dir + "6.jpg"),
				Texture.MinificationFilter.BilinearNearestMipMap,
				Texture.MagnificationFilter.Bilinear);
		Texture down = TextureManager.loadTexture(Bowling.class
				.getClassLoader().getResource(dir + "5.jpg"),
				Texture.MinificationFilter.BilinearNearestMipMap,
				Texture.MagnificationFilter.Bilinear);

		skybox.setTexture(Skybox.Face.North, north);
		skybox.setTexture(Skybox.Face.West, west);
		skybox.setTexture(Skybox.Face.South, south);
		skybox.setTexture(Skybox.Face.East, east);
		skybox.setTexture(Skybox.Face.Up, up);
		skybox.setTexture(Skybox.Face.Down, down);
		skybox.preloadTextures();

		CullState cullState = display.getRenderer().createCullState();
		cullState.setCullFace(CullState.Face.None);
		cullState.setEnabled(true);
		skybox.setRenderState(cullState);

		ZBufferState zState = display.getRenderer().createZBufferState();
		zState.setEnabled(false);
		skybox.setRenderState(zState);

		FogState fs = display.getRenderer().createFogState();
		fs.setEnabled(false);
		skybox.setRenderState(fs);

		skybox.setLightCombineMode(Spatial.LightCombineMode.Off);
		skybox.setCullHint(Spatial.CullHint.Never);
		skybox.setTextureCombineMode(TextureCombineMode.Replace);
		skybox.updateRenderState();

		skybox.lockBounds();
		skybox.lockMeshes();

		rootNode.attachChild(skybox);
	}
	
	private void createPins(){
		
		this.pinList= new ArrayList<Pin>(10);
		
		
		int x[]={0,PINS_SPACE,-PINS_SPACE, PINS_SPACE*2,0,-PINS_SPACE*2,PINS_SPACE*3,PINS_SPACE,-PINS_SPACE,-PINS_SPACE*3};
		int z[]={0,1,1,2,2,2,3,3,3,3};
		
		for(int i=0; i< PINS_COUNT; i++){
			DynamicPhysicsNode pinPhy = physicsSpace.createDynamicNode();
			pinPhy.setName("pin " + i);
	    	rootNode.attachChild(pinPhy);
	    	
	    	this.pinList.add(new Pin(pinPhy, new Vector3f(x[i], COURT_FLOOR, -1*(PINS_SITE + 12*z[i]))));
		}
    
	}
	
	private void createBall(){
		
		DynamicPhysicsNode ballPhy = physicsSpace.createDynamicNode();
		ballPhy.setName("ball");
    	rootNode.attachChild(ballPhy);
    	
    	this.ball= new Ball(ballPhy, new Vector3f(0,COURT_FLOOR, BALL_SITE));
    	
    
	}
	
	public void simpleUpdate(){
		skybox.getLocalTranslation().set(cam.getLocation());
		skybox.updateGeometricState(0.0f, true);
	}
	
	
	public void shotBall(float x, float y, float z){
		ball.shootMe(x ,y, z);
	}
	
	public void resetPins(){
		for(int i=0; i< PINS_COUNT; i++){
			pinList.get(i).resetPos();
		}
	}
	
	public void resetPlay(){
		resetPins();
		ball.resetPos();
	}
	
	
	public int getFallenPins() {
		int i=0;
		
		for(Pin p: pinList){
			if(p.isFallen()){
				rootNode.detachChild(p.getNode());
				p.setDetach(true);
				i++;
			}
		}
		
		return i;
		
	}

	public void attachAllPins(){
		for(Pin p: pinList){
			rootNode.attachChild(p.getNode());
			p.setDetach(false);
		}
	}


	public boolean isBallOut() {
		return ball.isOutTheCourt();
		
	}
	

	public boolean isBallStoped(Timer timer) {
		return ball.isStoped(timer);
	}
	
	public boolean ballPassMiddle(){
		boolean ans= false;
		
		if(ball.getNode().getLocalTranslation().getZ()<SCENE_SIZE/5){
			ans=true;
		}
		
		return ans;
	}
	

	

}
